//******************************************************************************************************
// Copyright 2008 Amazon Technologies, Inc.  
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 
// compliance with the License. 
//
// You may obtain a copy of the License at:http://aws.amazon.com/apache2.0  This file is distributed on 
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
//
// See the License for the specific language governing permissions and limitations under the License. 
//******************************************************************************************************using System;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace SQS
{
    class Route
    {
        public int      _hopCount;
        public string   _name;
        public string   _ip;
        public int      _milliseconds;
    }

    class RouteTrace
    {
        byte[]  _data;
        ICMP    _packet      = new ICMP();
        int     _packetSize;    
        int     _badCount = 0;
    
        public RouteTrace()
        {
            _packet.Type         = 0x08;
            _packet.Code         = 0x00;
            _packet.CheckSum     = 0;
            
            Buffer.BlockCopy(BitConverter.GetBytes(1), 0, _packet.Message, 0, 2);
            Buffer.BlockCopy(BitConverter.GetBytes(1), 0, _packet.Message, 2, 2);
            
            _data                = Encoding.ASCII.GetBytes("test packet");
            Buffer.BlockCopy(_data, 0, _packet.Message, 4, _data.Length);
            
            _packet.MessageSize  = _data.Length + 4;
            _packetSize          = _packet.MessageSize + 4;
        
            UInt16 checkSum = _packet.GetChecksum();
            _packet.CheckSum = checkSum;
        }
    
        public int BadCount
        {
            get
            {
                return _badCount;
            }
        }

        public ArrayList Trace(string destination)
        {
            return Trace(destination, 50);
        }
    
        public ArrayList Trace(string destination, int hops)
        {                                             
            _badCount = 0;

            Socket host     = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
            host.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000);

            ArrayList nodes = new ArrayList();
            IPHostEntry iphe = Dns.GetHostEntry(destination);
            IPEndPoint iep = new IPEndPoint(iphe.AddressList[0], 0);
            EndPoint ep = (EndPoint)iep;

            for (int i = 1; i < hops; i++)
            {
                host.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, i);
                int timeStart = Environment.TickCount;
                
                try
                {
                    host.SendTo(_packet.GetBytes(), _packetSize, SocketFlags.None, iep);
                }
                catch (Exception ex)
                {
                    if (ex is System.Net.Sockets.SocketException)
                    {
                        if (ex.Message == "An attempt was made to access a socket in a way forbidden by its access permissions")
                        {
                            throw new Exception("User account doesn't have ICMP network permissions.");
                        }
                    }

                    throw ex;
                }
                try
                {
                    _data     = new byte[1024];
                            
                    int recv     = host.ReceiveFrom(_data, ref ep);
                    int timestop = Environment.TickCount;
                    ICMP response = new ICMP(_data, recv);
                    
                    if (response.Type == 11)
                    {
                        Route r = new Route();

                        r._hopCount     = i;
                        r._milliseconds = timestop - timeStart;
                        r._name         = ep.ToString();
                        r._ip           = ep.ToString();
                        if (ep is IPEndPoint)
                        {
                            IPHostEntry entry;
                            IPEndPoint ip = (IPEndPoint)ep;
                            r._ip          = ip.ToString();
                            entry         = Dns.GetHostEntry(ip.Address);
                            if (entry != null) r._name = entry.HostName;
                        }
                        nodes.Add(r);
                    }
                    if (response.Type == 0)
                    {
                        Route r = new Route();
                        r._hopCount     = i;
                        r._milliseconds = timestop - timeStart;
                        r._name         = ep.ToString();
                        r._ip           = ep.ToString();
                        nodes.Add(r);
                        break;
                    }
                    _badCount = 0;
                } 
                catch (SocketException)
                {
                    _badCount++;
                    if (_badCount == 5) break;
                }
            }
        
            host.Close();
        
            return nodes;
        }
    
        public int Latency(string destination)
        {
            IPHostEntry     iphe        = Dns.GetHostEntry(destination);
            IPEndPoint      iep         = new IPEndPoint(iphe.AddressList[0], 0);
            EndPoint        ep          = (EndPoint)iep;
            
            int             results     = -1;
            int             badcount    = 0;

            Socket host = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
            host.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000);
        
            for (int i = 0; i < 50; i++)
            {
                host.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, i);
                int timestart = Environment.TickCount;
                host.SendTo(_packet.GetBytes(), _packetSize, SocketFlags.None, iep);
                try
                {
                    _data     = new byte[1024];
                    
                    int recv     = host.ReceiveFrom(_data, ref ep);
                    int timestop = Environment.TickCount;
                    ICMP response = new ICMP(_data, recv);
                    results = timestop - timestart;
                    
                    if (response.Type == 0) break;
                } 
                catch (SocketException)
                {
                    badcount++;
                    if (badcount == 5) break;
                }
            }
        
            host.Close();
        
            return results;
        }
    }

    class ICMP
    {
        byte    _type;
        byte    _code;
        UInt16  _checkSum;
        int     _messageSize;
        byte[]  _message = new byte[1024];
    
        public ICMP()
        {
        }
    
        public ICMP(byte[] data, int size)
        {
            _type = data[20];
            _code = data[21];
            _checkSum = BitConverter.ToUInt16(data, 22);
            _messageSize = size - 24;
            
            Buffer.BlockCopy(data, 24, _message, 0, _messageSize);
        }

        public byte Type
        {
            get { return _type; }
            set { _type = value; }
        }

        public byte Code
        {
            get { return _code; }
            set { _code = value; }
        }

        public UInt16 CheckSum
        {
            get { return _checkSum; }
            set { _checkSum = value; }
        }

        public int MessageSize
        {
            get { return _messageSize; }
            set { _messageSize = value; }
        }

        public byte[] Message
        {
            get { return _message; }
            set { _message = value; }
        }

        public byte[] GetBytes()
        {
            byte[] data = new byte[MessageSize + 9];
        
            Buffer.BlockCopy(BitConverter.GetBytes(Type), 0, data, 0, 1);
            Buffer.BlockCopy(BitConverter.GetBytes(Code), 0, data, 1, 1);
            Buffer.BlockCopy(BitConverter.GetBytes(CheckSum), 0, data, 2, 2);
            Buffer.BlockCopy(Message, 0, data, 4, MessageSize);
        
            return data;
        }
    
        public UInt16 GetChecksum()
        {
            UInt32 checkSum = 0;
            byte[] data = GetBytes();
            int packetsize = MessageSize + 8;
            int index = 0;
        
            while (index < packetsize)
            {
                checkSum += Convert.ToUInt32(BitConverter.ToUInt16(data, index));
                index += 2;
            }
        
            checkSum = (checkSum >> 16) + (checkSum & 0xffff);
            checkSum += (checkSum >> 16);

            return (UInt16)(~checkSum);
        }
    }
}
